package ia.robot.gui;

import ia.robot.app.Board;
import ia.robot.app.Options;
import ia.robot.app.Point;
import ia.robot.app.Robot;
import ia.robot.graphics.Textures;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.filechooser.FileNameExtensionFilter;

public class MainFrame extends JFrame implements ActionListener, MouseListener,
		MouseMotionListener {

	private static final long serialVersionUID = 1L;

	private Board board;

	private JFileChooser fileChooser;

	private MainCanvasPane canvas;
	private MainControlPane control;
	private MainLogPane logger;
	private MainMenu menu;

	private Point pDiff;

	private boolean locked;

	public MainFrame(String name) {
		locked = false;

		this.setTitle(name);
		this.setSize(960, 720);
		this.setLocationRelativeTo(null);
		this.setLayout(new BorderLayout(5, 5));

		fileChooser = new JFileChooser("./maps/");
		fileChooser.setFileFilter(new FileNameExtensionFilter("Board files",
				"board"));

		board = new Board(10, 10);

		canvas = new MainCanvasPane(10, 10);
		canvas.addMouseListener(this);
		canvas.addMouseMotionListener(this);
		canvas.blitBitmap(board.getBitmap());

		control = new MainControlPane();
		control.addButtonListener(this);

		logger = new MainLogPane();
		logger.addButtonListener(this);

		menu = new MainMenu();
		menu.addMenuListener(this);

		this.setJMenuBar(menu);
		this.getContentPane().add(canvas, BorderLayout.CENTER);
		this.getContentPane().add(control, BorderLayout.EAST);
		this.getContentPane().add(logger, BorderLayout.SOUTH);
	}

	public void actionPerformed(ActionEvent e) {
		if (!locked) {
			String actionCommand = e.getActionCommand();

			// Si el evento fue lanzado por un boton
			if (e.getSource() instanceof JButton) {
				// Si el evento fue lanzado por el boton Add Timestamp
				if (actionCommand.equals(MainLogPane.timeStampString)) {
					logger.write(new Date().toString() + "\n");
				}

				// Si el evento fue lanzado por el boton Clear Log
				else if (actionCommand.equals(MainLogPane.clearLogString)) {
					logger.clear();
				}

				// Si el evento fue lanzado por el boton Randomize
				else if (actionCommand.equals(MainControlPane.randomizeString)) {
					board.randomize();

					canvas.setPath(null);
					canvas.blitBitmap(board.getBitmap());
				}

				// Si el evento fue lanzado por el boton Run
				else if (actionCommand.equals(MainControlPane.runString)) {
					if (board.getBotPos() == null) {
						logger.write("Error - Robot not present on board...\n");

						return;
					}

					if (board.getDestPos() == null) {
						logger.write("Error - Destination not present on board...\n");

						return;
					}

					Options options = new Options();
					options.setDebug(control.getDebug());
					options.setDelay(control.getDelay());
					options.setHeuristic(control.getHeuristic());
					options.setSearchMethod(control.getSearchMethod());

					canvas.setPath(null);
					
					Robot r = new Robot(board, canvas, logger, this, options);
					r.start();
				}
				
				//Si el evento fue lanzado por el boton test
				else if (actionCommand.equals(MainControlPane.testString)) {
					logger.write("Initiating test mode...\n");
					canvas.setPath(null);
					Options options = new Options();
					options.setDebug(control.getDebug());
					options.setDelay(0);
					Robot r = new Robot(board, canvas, logger, this, options);
					for (int i = 10; i < 40; i++) {
						if (i == 10)
							logger.write("Loading 20x20 maps for testing:\n");
						if (i == 20)
							logger.write("Loading 40x40 maps for testing:\n");
						if (i == 30)
							logger.write("Loading 80x80 maps for testing:\n");
						loadBoard("./maps/test" + i + ".board");
						logger.write(" map " + i + ",");
						r.setNewBoard(this.board);
						r.test();
						if ((i == 19) || (i == 29) || (i == 39)) {
							logger.write("\nTest complete:" + "\n");
							logger.write("Average Depth steps: " + r.getAverageDepthSteps() + "\n");
							logger.write("Average Depth path steps: " + r.getAverageDepthPathSteps() + "\n");
							logger.write("Average Euclidean steps: " + r.getAverageEuclideanSteps() + "\n");
							logger.write("Average Euclidean path steps: " + r.getAverageEuclideanPathSteps() + "\n");
							logger.write("Average Manhattan steps: " + r.getAverageManhattanSteps() + "\n");
							logger.write("Average Manhattan path steps: " + r.getAverageManhattanPathSteps() + "\n");
							logger.write("Average Mahalanobis steps: " + r.getAverageMahalanobisSteps() + "\n");
							logger.write("Average Mahalanobis path steps: " + r.getAverageMahalanobisPathSteps() + "\n");
							r.resetAverages();
						}
					}
				}
			}

			// Si el evento fue lanzado por un menu
			if (e.getSource() instanceof JMenuItem) {
				// Si el evento fue lanzado por el menu New
				if (actionCommand.equals(MainMenu.newString)) {
					NewDialog newDialog = new NewDialog(this);

					Dimension d = newDialog.getDimension();

					if (d != null) {
						board = new Board(d.width, d.height);

						canvas.resizeCanvas(d.width, d.height);
						canvas.blitBitmap(board.getBitmap());
					}
				}

				// Si el evento fue lanzado por el menu Load
				else if (actionCommand.equals(MainMenu.loadString)) {
					int retValue = fileChooser.showOpenDialog(this);

					if (retValue == JFileChooser.APPROVE_OPTION) {
						File myFile = fileChooser.getSelectedFile();

						FileInputStream fis;
						ObjectInputStream ois;

						try {
							fis = new FileInputStream(myFile);
							ois = new ObjectInputStream(fis);

							board = (Board) ois.readObject();
							canvas.resizeCanvas(board.getWidth(),
									board.getHeight());
							canvas.blitBitmap(board.getBitmap());

							ois.close();
							fis.close();

							logger.write("Loaded " + myFile.getName() + ".\n");
						} catch (FileNotFoundException ex) {
							ex.printStackTrace();
						} catch (IOException ex) {
							ex.printStackTrace();
						} catch (ClassNotFoundException ex) {
							ex.printStackTrace();
						}
					}
				}

				// Si el evento fue lanzado por el menu Save
				else if (actionCommand.equals(MainMenu.saveString)) {
					int retValue = fileChooser.showSaveDialog(this);

					if (retValue == JFileChooser.APPROVE_OPTION) {
						File myFile = fileChooser.getSelectedFile();

						FileOutputStream fos;
						ObjectOutputStream oos;

						try {
							fos = new FileOutputStream(myFile);
							oos = new ObjectOutputStream(fos);

							oos.writeObject(board);

							oos.close();
							fos.close();

							logger.write("Saved " + myFile.getName() + ".\n");
						} catch (FileNotFoundException ex) {
							ex.printStackTrace();
						} catch (IOException ex) {
							ex.printStackTrace();
						}
					}
				}

				// Si el evento fue lanzado por el menu Quit
				else if (actionCommand.equals(MainMenu.quitString)) {
					System.exit(0);
				}
			}
		}
	}

	public void mouseClicked(MouseEvent e) {

	}

	public void mouseDragged(MouseEvent e) {
		if (!locked) {
			if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == MouseEvent.BUTTON1_DOWN_MASK) {
				int x = e.getX() / Textures.TILE_WIDTH;
				int y = e.getY() / Textures.TILE_HEIGHT;

				Point p = new Point(x, y);

				if ((pDiff == null) || !(pDiff.equals(p))) {
					pDiff = p;

					switch (control.getButtonPressed()) {
					case Textures.TILE_GRASS:
						board.setContent(p, Textures.TILE_GRASS);
						canvas.blitBitmap(board.getBitmap());
						break;
					case Textures.TILE_STONE:
						board.setContent(p, Textures.TILE_STONE);

						if (p.equals(board.getBotPos()))
							board.setBotPos(null);

						if (p.equals(board.getDestPos()))
							board.setDestPos(null);

						canvas.blitBitmap(board.getBitmap());
						break;
					default:
						break;
					}
				}
			}
		}
	}

	public void mouseEntered(MouseEvent e) {

	}

	public void mouseExited(MouseEvent e) {

	}

	public void mouseMoved(MouseEvent e) {

	}

	public void mousePressed(MouseEvent e) {
		if (!locked) {
			if (e.getButton() == MouseEvent.BUTTON1) {
				int x = e.getX() / Textures.TILE_WIDTH;
				int y = e.getY() / Textures.TILE_HEIGHT;

				Point p = new Point(x, y);

				switch (control.getButtonPressed()) {
				case Textures.TILE_GRASS:
					board.setContent(p, Textures.TILE_GRASS);
					canvas.blitBitmap(board.getBitmap());
					break;
				case Textures.TILE_STONE:
					board.setContent(p, Textures.TILE_STONE);

					if (p.equals(board.getBotPos()))
						board.setBotPos(null);

					if (p.equals(board.getDestPos()))
						board.setDestPos(null);

					canvas.blitBitmap(board.getBitmap());
					break;
				case Textures.TILE_BOT_SOUTH:
					board.setContent(p, Textures.TILE_GRASS);
					board.setBotPos(p);
					board.setBotDir(Point.SOUTH);
					canvas.blitBitmap(board.getBitmap());
					break;
				case Textures.TILE_GOAL:
					board.setContent(p, Textures.TILE_GRASS);
					board.setDestPos(p);
					canvas.blitBitmap(board.getBitmap());
					break;
				default:
					break;
				}
			}
		}
	}

	public void mouseReleased(MouseEvent e) {

	}

	public synchronized void lockGUI() {
		locked = true;

		menu.lockMainMenu();
		logger.lockLogPane();
		control.lockControlPane();
	}

	public synchronized void unlockGUI() {
		locked = false;

		menu.unlockMainMenu();
		logger.unlockLogPane();
		control.unlockControlPane();
	}
	
	public void loadBoard (String filename) {
		File myFile = new File (filename);

		FileInputStream fis;
		ObjectInputStream ois;

		try {
			fis = new FileInputStream(myFile);
			ois = new ObjectInputStream(fis);

			board = (Board) ois.readObject();
			canvas.resizeCanvas(board.getWidth(), board.getHeight());
			canvas.blitBitmap(board.getBitmap());

			ois.close();
			fis.close();

		} catch (FileNotFoundException ex) {
			ex.printStackTrace();
		} catch (IOException ex) {
			ex.printStackTrace();
		} catch (ClassNotFoundException ex) {
			ex.printStackTrace();
		}
	}
}
